# Logistics · Customer

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## POST /client/logistics/quote

**Get a delivery quote**

`operationId: DeliveryClientController_getPriceQuote`

Prices a delivery from its stops. Fast path — use the validated form when the addresses have not been checked.

#### Signature

```http
POST /client/logistics/quote (body) -> The quote
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/quote/validated`

### 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 stops.

```json
{
  "stops": [
    {
      "type": "pickup",
      "location": {
        "lat": 37.7936,
        "lng": -122.3958
      }
    },
    {
      "type": "dropoff",
      "location": {
        "lat": 37.7879,
        "lng": -122.3972
      }
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The quote |
| `401` | Not authenticated — No customer or user 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/logistics/quote/validated

**Get a validated delivery quote**

`operationId: DeliveryClientController_getValidatedPriceQuote`

Geocodes and validates each address before pricing, so an unreachable address is caught here rather than after a driver has been dispatched. Slower than the plain quote, and the one to use before creating an order.

#### Signature

```http
POST /client/logistics/quote/validated (body) -> The validated quote
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/orders`

### 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 stops.

```json
{
  "stops": [
    {
      "type": "pickup",
      "location": {
        "address": "1 Market St, San Francisco, CA"
      }
    },
    {
      "type": "dropoff",
      "location": {
        "address": "500 Howard St, San Francisco, CA"
      }
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The validated quote |
| `401` | Not authenticated — No customer or user 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/logistics/jobs

**Create a delivery job**

`operationId: DeliveryClientController_createJob`

Creates a job as the calling customer. Unpaid — use `POST /client/logistics/orders` for the flow that also sets up payment.

#### Signature

```http
POST /client/logistics/jobs (body) -> The job
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/orders`

### 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 job.

```json
{
  "stops": [
    {
      "type": "pickup",
      "location": {
        "address": "1 Market St, San Francisco, CA"
      }
    },
    {
      "type": "dropoff",
      "location": {
        "address": "500 Howard St, San Francisco, CA"
      }
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The job |
| `401` | Not authenticated — No customer or user 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/logistics/stripe/config

**Get the Stripe client configuration**

`operationId: DeliveryClientController_getStripeConfig`

The publishable key and options a client needs to mount Stripe. Publishable values only — no secret key is exposed here.

#### Signature

```http
GET /client/logistics/stripe/config () -> Stripe client config
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/stripe/intent`

### 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` | Stripe client config |
| `401` | Not authenticated — No customer or user 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/logistics/stripe/intent

**Create a Stripe payment intent**

`operationId: DeliveryClientController_stripePaymentIntent`

Creates a payment intent and returns its client secret for the browser to confirm. The confirmation happens client-side against Stripe; the server learns the outcome through `stripe/verify` or the order completion endpoint.

#### Signature

```http
POST /client/logistics/stripe/intent (body) -> The intent and client secret
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/stripe/verify`

### 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

What to charge.

```json
{
  "jobId": "JOB-4821",
  "amount": 3200
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The intent and client secret |
| `401` | Not authenticated — No customer or user 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/logistics/stripe/verify

**Verify a Stripe payment**

`operationId: DeliveryClientController_verifyStripePayment`

Confirms with Stripe that a payment intent actually succeeded, rather than trusting the browser's word for it. This is the check that stops a client claiming an unpaid delivery was paid.

#### Signature

```http
POST /client/logistics/stripe/verify (body) -> The verification result
```

#### Access

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

#### Notes

- Server-side verification — do not skip it.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/complete-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. |

### Request body

The intent to verify.

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The verification result |
| `401` | Not authenticated — No customer or user 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/logistics/paypal/create

**Create a PayPal order**

`operationId: DeliveryClientController_createPayPalOrder`

Creates a PayPal order for the customer to approve in their browser.

#### Signature

```http
POST /client/logistics/paypal/create (body) -> The PayPal order
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/paypal/capture`

### 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

What to charge.

```json
{
  "jobId": "JOB-4821",
  "amount": 3200
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The PayPal order |
| `401` | Not authenticated — No customer or user 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/logistics/paypal/capture

**Capture a PayPal order**

`operationId: DeliveryClientController_capturePayPalOrder`

Captures an approved PayPal order — this is the point the money actually moves. Approval alone does not charge the customer.

#### Signature

```http
POST /client/logistics/paypal/capture (body) -> The capture result
```

#### Access

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

#### Notes

- Charges the customer.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/paypal/create`

### 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 order to capture.

```json
{
  "paypalOrderId": "5O190127TN364715T"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The capture result |
| `401` | Not authenticated — No customer or user 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/logistics/orders

**List my orders**

`operationId: DeliveryClientController_getMyDeliveryOrders`

Deliveries the caller ordered, with their status.

#### Signature

```http
GET /client/logistics/orders (status?: string, page?: integer, pageSize?: integer) -> Orders
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `GET /client/logistics/orders/{jobId}/track`

### 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 | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Orders |
| `401` | Not authenticated — No customer or user 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/logistics/orders

**Create a delivery order**

`operationId: DeliveryClientController_createDeliveryOrder`

The customer-facing order flow: creates the job and starts payment with the chosen provider in one call. Each stop carries a location, a contact and the items being moved.

Creating the order does **not** complete payment — the client must confirm with Stripe or PayPal and then call `complete-payment`. Until that happens the delivery is not paid for.

#### Signature

```http
POST /client/logistics/orders (body) -> The order and payment setup
```

#### Access

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

#### Notes

- Payment must still be confirmed and completed.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/complete-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. |

### Request body

The order.

```json
{
  "stops": [
    {
      "type": "pickup",
      "location": {
        "lat": 37.7936,
        "lng": -122.3958
      },
      "contact": {
        "name": "Ada Lovelace",
        "phone": "+15551234567"
      },
      "items": [
        {
          "description": "Sealed box",
          "quantity": 2
        }
      ]
    },
    {
      "type": "dropoff",
      "location": {
        "lat": 37.7879,
        "lng": -122.3972
      },
      "contact": {
        "name": "Grace Hopper",
        "phone": "+15559876543"
      },
      "instructions": "Leave with reception"
    }
  ],
  "paymentMethod": "stripe"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The order and payment setup |
| `401` | Not authenticated — No customer or user 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/logistics/orders/{jobId}/complete-payment

**Complete an order payment**

`operationId: DeliveryClientController_completeDeliveryPayment`

Finalises payment after the customer confirmed with Stripe or approved with PayPal. Pass the provider reference — `paymentIntentId` for Stripe, `paypalOrderId` for PayPal — which the server verifies against the provider rather than taking on trust.

#### Signature

```http
POST /client/logistics/orders/{jobId}/complete-payment (jobId: string, body) -> The payment result
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/payment-intent`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Request body

The provider confirmation.

```json
{
  "paymentMethod": "stripe",
  "paymentIntentId": "pi_3Abc123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The payment result |
| `401` | Not authenticated — No customer or user 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` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}/payment-intent

**Create a payment intent for an order**

`operationId: DeliveryClientController_createOrderPaymentIntent`

Creates a fresh payment intent for an existing order — for retrying after a failed or abandoned attempt without re-creating the delivery.

#### Signature

```http
POST /client/logistics/orders/{jobId}/payment-intent (jobId: string) -> The intent
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/complete-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. |
| `jobId` | path | string | yes | Delivery job id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The intent |
| `401` | Not authenticated — No customer or user 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` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}/cancel

**Cancel an order**

`operationId: DeliveryClientController_cancelDeliveryOrder`

Cancels the caller's delivery. Whether anything is refunded depends on the org's cancellation policy and how far the job has progressed — a cancellation after pickup usually is not free.

#### Signature

```http
PUT /client/logistics/orders/{jobId}/cancel (jobId: string, body) -> The cancelled order
```

#### Access

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

#### Notes

- A cancellation fee may apply.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |
| `403` | NOT_AUTHORIZED_CANCEL | Not authorized to cancel this job | The job is not the caller's. | Only the ordering customer can cancel here. |

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

#### See also

- `GET /client/logistics/orders`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Request body

Why.

```json
{
  "reason": "No longer needed"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The cancelled order |
| `401` | Not authenticated — No customer or user could be resolved from the token. |
| `403` | Not authorized to cancel this job — The job is not the caller's. |
| `404` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}

**Update an order**

`operationId: DeliveryClientController_updateDeliveryOrder`

Changes an order before a driver is on the way — addresses, contacts, instructions. Once the pickup leg has started the details are in the driver's hands and changes here may not reach them; message the driver instead.

#### Signature

```http
PUT /client/logistics/orders/{jobId} (jobId: string, body) -> The updated order
```

#### Access

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

#### Notes

- Late changes may not reach the driver.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |

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

#### See also

- `POST /client/logistics/jobs/{jobId}/messages`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Request body

Fields to change.

```json
{
  "notes": "Buzzer 4B, not 4A"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The updated order |
| `401` | Not authenticated — No customer or user 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` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}/track

**Track an order**

`operationId: DeliveryClientController_trackDeliveryOrder`

Live status and driver position for a delivery — what a customer's tracking screen polls.

#### Signature

```http
GET /client/logistics/orders/{jobId}/track (jobId: string) -> Tracking state
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |

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

#### See also

- `PUT /client/logistics/jobs/{jobId}/tracking`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Tracking state |
| `401` | Not authenticated — No customer or user 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` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}/rate

**Rate a delivery**

`operationId: DeliveryClientController_rateDelivery`

Rates a completed delivery. Only completed deliveries can be rated, and only by the customer who ordered it. Ratings feed the driver's performance score, which affects the jobs they are offered — so this is consequential for the driver, not just feedback.

#### Signature

```http
POST /client/logistics/orders/{jobId}/rate (jobId: string, body) -> The rating
```

#### Access

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

#### Notes

- Affects the driver's standing.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |
| `400` | NOT_COMPLETED | Can only rate completed deliveries | The delivery is not finished. | Wait until it completes. |
| `403` | NOT_AUTHORIZED_RATE | Not authorized to rate this delivery | The caller did not order it. | Only the ordering customer can rate. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/tip`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Request body

The rating.

```json
{
  "rating": 5,
  "comment": "Fast and careful."
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The rating |
| `400` | Can only rate completed deliveries — The delivery is not finished. |
| `401` | Not authenticated — No customer or user could be resolved from the token. |
| `403` | Not authorized to rate this delivery — The caller did not order it. |
| `404` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}/tip

**Tip a driver**

`operationId: DeliveryClientController_addTip`

Starts a tip on a completed delivery, returning a payment intent or PayPal order to confirm. The tip is not charged until `tip/complete` runs.

#### Signature

```http
POST /client/logistics/orders/{jobId}/tip (jobId: string, body) -> The tip payment setup
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |
| `400` | NOT_COMPLETED | Can only tip on completed deliveries | The delivery is not finished. | Wait until it completes. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/tip/complete`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Request body

The tip.

```json
{
  "amount": 500,
  "paymentMethod": "stripe"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The tip payment setup |
| `400` | Can only tip on completed deliveries — The delivery is not finished. |
| `401` | Not authenticated — No customer or user 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` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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/logistics/orders/{jobId}/tip/complete

**Complete a tip payment**

`operationId: DeliveryClientController_completeTip`

Finalises a tip after the customer confirmed with the provider. This is where the tip is actually charged and credited to the driver — without it the tip was only ever intended.

#### Signature

```http
POST /client/logistics/orders/{jobId}/tip/complete (jobId: string, body) -> The tip result
```

#### Access

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

#### Notes

- Charges the customer and credits the driver.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `401` | NOT_AUTHENTICATED | Not authenticated | No customer or user could be resolved from the token. | Sign in. |
| `404` | JOB_NOT_FOUND | Delivery job not found | No job has that id, or it is not the caller's. | List your jobs with `GET /client/logistics/jobs`. |

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

#### See also

- `POST /client/logistics/orders/{jobId}/tip`

### 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. |
| `jobId` | path | string | yes | Delivery job id. |

### Request body

The confirmation.

```json
{
  "amount": 500,
  "paymentIntentId": "pi_3Abc123"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The tip result |
| `401` | Not authenticated — No customer or user 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` | Delivery job not found — No job has that id, or it is not the caller's. |
| `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. |

