diff --git a/postman/collections/Fleetbase API/Entities/Create an Entity.request.yaml b/postman/collections/Fleetbase API/Entities/Create an Entity.request.yaml index 9c7b91c..7481a8c 100644 --- a/postman/collections/Fleetbase API/Entities/Create an Entity.request.yaml +++ b/postman/collections/Fleetbase API/Entities/Create an Entity.request.yaml @@ -1,6 +1,8 @@ $kind: http-request description: |- Creates an entity such as a parcel, package, or item. Attach it to a payload and optionally set a destination or waypoint within that payload route. + + When the entity belongs to an order's payload, the order's status decides whether its items can still change: once a driver has the order, or the order is finished, items are locked; once it is ready, items can only be removed. A locked change returns `422` `{ "error": "...", "locked": { "items": "" } }`, and an accepted change broadcasts `order.changed`. Requires Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72). url: "{{base_url}}/{{namespace}}/entities" method: POST headers: diff --git a/postman/collections/Fleetbase API/Entities/Delete a Entity.request.yaml b/postman/collections/Fleetbase API/Entities/Delete a Entity.request.yaml index eb79034..20f87ff 100644 --- a/postman/collections/Fleetbase API/Entities/Delete a Entity.request.yaml +++ b/postman/collections/Fleetbase API/Entities/Delete a Entity.request.yaml @@ -1,5 +1,8 @@ $kind: http-request -description: Delete an Entity. +description: |- + Delete an Entity. + + When the entity belongs to an order's payload, the order's status decides whether its items can still change: once a driver has the order, or the order is finished, items are locked; once it is ready, items can only be removed. A locked change returns `422` `{ "error": "...", "locked": { "items": "" } }`, and an accepted change broadcasts `order.changed`. Requires Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72). url: "{{base_url}}/{{namespace}}/entities/:id" method: DELETE headers: diff --git a/postman/collections/Fleetbase API/Entities/Update a Entity.request.yaml b/postman/collections/Fleetbase API/Entities/Update a Entity.request.yaml index 6dee422..ba70ba8 100644 --- a/postman/collections/Fleetbase API/Entities/Update a Entity.request.yaml +++ b/postman/collections/Fleetbase API/Entities/Update a Entity.request.yaml @@ -1,6 +1,8 @@ $kind: http-request description: |- Updates an entity's descriptive fields, payload assignment, destination, dimensions, weight, pricing, or metadata. + + When the entity belongs to an order's payload, the order's status decides whether its items can still change: once a driver has the order, or the order is finished, items are locked; once it is ready, items can only be removed. A locked change returns `422` `{ "error": "...", "locked": { "items": "" } }`, and an accepted change broadcasts `order.changed`. Requires Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72). url: "{{base_url}}/{{namespace}}/entities/:id" method: PUT headers: diff --git a/postman/collections/Fleetbase API/Orders/.resources/Update an Order.resources/examples/Unprocessable Content.example.yaml b/postman/collections/Fleetbase API/Orders/.resources/Update an Order.resources/examples/Unprocessable Content.example.yaml new file mode 100644 index 0000000..38e058a --- /dev/null +++ b/postman/collections/Fleetbase API/Orders/.resources/Update an Order.resources/examples/Unprocessable Content.example.yaml @@ -0,0 +1,37 @@ +$kind: http-example +request: + url: "{{base_url}}/{{namespace}}/orders/:id" + method: PUT + headers: + Content-Type: application/json + Accept: application/json + pathVariables: + id: "order_7YqM3KpL2n" + body: + type: json + content: |- + { + "scheduled_at": "2026-10-13T15:00:00Z", + "payload": { + "entities_mode": "replace", + "entities": [ + { "id": "entity_4Fr8Kq2Lp1", "name": "Cold Brew", "quantity": 3 } + ] + } + } +response: + statusCode: 422 + statusText: Unprocessable Content + headers: + Content-Type: application/json + body: + type: json + content: |- + { + "error": "Items cannot be changed once a driver has the order. The schedule cannot be changed once a driver has the order.", + "locked": { + "items": "Items cannot be changed once a driver has the order.", + "schedule": "The schedule cannot be changed once a driver has the order." + } + } +order: 2000 diff --git a/postman/collections/Fleetbase API/Orders/.resources/definition.yaml b/postman/collections/Fleetbase API/Orders/.resources/definition.yaml index be97f22..7dbd717 100644 --- a/postman/collections/Fleetbase API/Orders/.resources/definition.yaml +++ b/postman/collections/Fleetbase API/Orders/.resources/definition.yaml @@ -1,4 +1,15 @@ $kind: collection description: |- Orders represent work to be planned, dispatched, tracked, and completed. Use order endpoints to create delivery or service jobs, assign resources, manage status, capture proof of delivery, and record activity. + + ## Ad hoc orders + + A driver assigned to an order, by dispatch or auto-assign, has the order; there is nothing for the driver to accept. Only ad hoc orders (`adhoc: true`) are offered to drivers: dispatching one notifies the drivers within `adhoc_distance`, and Fleet-Ops notifies them again every minute until a driver takes it. The first driver to take it does so with `Start an Order`, sending their driver id as `assign`. + + ## Realtime events + + Order changes are broadcast on the socket channels below. Every event has the envelope `{ id, api_version, event, created_at, data }`. These events require Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72). + + - `order.changed`: the order's route, items, contact, schedule, notes or amounts were edited after it was placed (through `Update an Order`, `Update a Payload`, the entity endpoints, the console or Storefront). Channels: `order.{public_id}` and `order.{uuid}`, `driver.{public_id}` and `driver.{uuid}` of the assigned driver, `customer.{public_id}` and `company.{uuid}`. Data: `{ id, changes, summary, dropoff: { id, address, location }, cod_amount, total, currency, changed_by, changed_at }`; `changes` lists the parts that changed (`pickup`, `dropoff`, `return`, `waypoints`, `items`, `schedule`, `contact`, `notes`, `cod_amount`, ...) and `summary` is one readable line. When the change concerns the driver, the assigned driver also gets an `order_changed` push. + - `order.driver_unassigned`: a driver was taken off the order (unassigned or replaced). Channels: `driver.{public_id}` and `driver.{uuid}` of the previous driver, and `company.{uuid}`. Data: `{ id, driver }`. The previous driver also gets an `order_unassigned` push. order: 8000 diff --git a/postman/collections/Fleetbase API/Orders/Cancel an Order.params.yaml b/postman/collections/Fleetbase API/Orders/Cancel an Order.params.yaml new file mode 100644 index 0000000..9221127 --- /dev/null +++ b/postman/collections/Fleetbase API/Orders/Cancel an Order.params.yaml @@ -0,0 +1,5 @@ +$kind: params +fields: + - name: reason + type: string + description: "Why the order is canceled. Recorded as the canceled status details and sent with the canceled notifications. Trimmed to 1000 characters." diff --git a/postman/collections/Fleetbase API/Orders/Cancel an Order.request.yaml b/postman/collections/Fleetbase API/Orders/Cancel an Order.request.yaml index 34c71d9..1327c90 100644 --- a/postman/collections/Fleetbase API/Orders/Cancel an Order.request.yaml +++ b/postman/collections/Fleetbase API/Orders/Cancel an Order.request.yaml @@ -1,10 +1,21 @@ $kind: http-request -description: Cancels an order without deleting the order resource. +description: |- + Cancels an order without deleting the order resource. + + An optional `reason` (body or query string) becomes the details of the canceled tracking status and is sent with the canceled notifications: the assigned driver's push carries it as `reason`, so the Navigator app can show why the order was canceled. A reason is trimmed to 1000 characters. The `reason` requires Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72); earlier versions ignore it. url: "{{base_url}}/{{namespace}}/orders/:id/cancel" method: DELETE +headers: + Content-Type: application/json pathVariables: - key: id value: "{{order_id}}" description: (Required) The id of the `Order`. +body: + type: json + content: |- + { + "reason": "The customer asked to cancel." + } examples: .resources/Cancel an Order.resources/examples order: 13000 diff --git a/postman/collections/Fleetbase API/Orders/Update an Order.params.yaml b/postman/collections/Fleetbase API/Orders/Update an Order.params.yaml index 2ab4096..0934573 100644 --- a/postman/collections/Fleetbase API/Orders/Update an Order.params.yaml +++ b/postman/collections/Fleetbase API/Orders/Update an Order.params.yaml @@ -17,7 +17,15 @@ fields: description: Replacement waypoint list. When sent empty with route endpoint fields, existing waypoints are removed. - name: entities type: array of objects - description: Replacement entities for the order payload. + description: Entities for the order payload. Appended to the payload's entities, or with `entities_mode` `replace`, the payload's full list. + - name: entities_mode + type: enum + values: ["append", "replace"] + default: append + description: "`replace` (or `sync`) makes `entities` the payload's full list: entries with the `id` of one of the payload's entities update it, entries without one are created, and entities left out are deleted. Also read from `payload.entities_mode`." + - name: cod_amount + type: number + description: "Cash to collect on delivery, also accepted as `payload.cod_amount`. `0` clears it; a value that isn't numeric is ignored." - name: service_quote type: string description: Service quote ID to purchase and attach to the order. diff --git a/postman/collections/Fleetbase API/Orders/Update an Order.request.yaml b/postman/collections/Fleetbase API/Orders/Update an Order.request.yaml index adb3e6e..01e6a81 100644 --- a/postman/collections/Fleetbase API/Orders/Update an Order.request.yaml +++ b/postman/collections/Fleetbase API/Orders/Update an Order.request.yaml @@ -1,6 +1,19 @@ $kind: http-request description: |- Updates an order and returns the updated order resource. You can update order metadata, notes, status, assignment fields, scheduling fields, proof-of-delivery settings, and payload details. + + What can still change depends on the order's status. A change the status locks is refused with `422` and nothing is written: `{ "error": "...", "locked": { "": "" } }`. The fields are `pickup`, `dropoff`, `return`, `waypoints`, `items` (the payload's entities), `schedule` (`scheduled_at`), `contact` (`customer`), `notes` and `cod_amount`: + + - Open (`created`, `pending`, `accepted`, `confirmed`, `preparing`, or a custom status before dispatch): everything can change. + - Ready (`ready`, `pickup_ready`, `ready_for_pickup`): items can only be removed and the schedule can only move earlier; everything else can change. + - With a driver (dispatched, started, `enroute`, `driver_*` and similar, or a custom status after dispatch): the route (the driver is rerouted), contact, notes and `cod_amount` can change; items and schedule are locked. + - Finished (`completed`, `canceled`, `expired`, or the order config's complete or terminal activity): nothing can change. + + Entities sent with the payload are appended by default. With `entities_mode` `replace` (at the top level or in `payload`), the list sent becomes the payload's full list: entries with an `id` of one of the payload's entities update it, entries without one are created, and entities left out are deleted. `cod_amount` is accepted at the top level or in `payload`, and `0` is kept (it clears the cash to collect). The order's current waypoint only moves when the route changed, so an order under way keeps its stop. + + An edit that changes anything broadcasts the realtime event `order.changed` (see the Orders folder) and, when the change concerns the driver, sends the assigned driver an `order_changed` push. + + The status guard, `entities_mode` and top-level `cod_amount` require Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72). url: "{{base_url}}/{{namespace}}/orders/:id" method: PUT pathVariables: diff --git a/postman/collections/Fleetbase API/Payloads/Update a Payload.params.yaml b/postman/collections/Fleetbase API/Payloads/Update a Payload.params.yaml index b65df7f..2abe996 100644 --- a/postman/collections/Fleetbase API/Payloads/Update a Payload.params.yaml +++ b/postman/collections/Fleetbase API/Payloads/Update a Payload.params.yaml @@ -14,7 +14,12 @@ fields: description: Replacement waypoint list. Sending route endpoint fields without waypoints removes existing waypoints. - name: entities type: array of objects - description: Replacement entities, packages, or items attached to the payload. + description: Entities, packages, or items for the payload. Appended to its entities, or with `entities_mode` `replace`, the payload's full list. + - name: entities_mode + type: enum + values: ["append", "replace"] + default: append + description: "`replace` (or `sync`) makes `entities` the payload's full list: entries with the `id` of one of the payload's entities update it, entries without one are created, and entities left out are deleted." - name: type type: string description: Payload type. @@ -23,7 +28,7 @@ fields: description: Provider identifier associated with the payload. - name: cod_amount type: currency - description: Cash-on-delivery amount. + description: Cash-on-delivery amount. `0` is kept and clears the cash to collect. - name: cod_currency type: currency description: Three-letter currency code for `cod_amount`. diff --git a/postman/collections/Fleetbase API/Payloads/Update a Payload.request.yaml b/postman/collections/Fleetbase API/Payloads/Update a Payload.request.yaml index 59e024b..99c5f9e 100644 --- a/postman/collections/Fleetbase API/Payloads/Update a Payload.request.yaml +++ b/postman/collections/Fleetbase API/Payloads/Update a Payload.request.yaml @@ -1,6 +1,10 @@ $kind: http-request description: |- Updates a payload's route endpoints, waypoints, entities, cash-on-delivery settings, or metadata. The response returns the updated payload with route and entity data. + + Entities are appended by default; with `entities_mode` `replace` the list sent becomes the payload's full list (entries with the `id` of one of its entities update it, entries without one are created, and entities left out are deleted). A `cod_amount` of `0` is kept, clearing the cash to collect; other empty values are ignored as before. + + When the payload belongs to an order, the order's status decides what can still change, as for `Update an Order`: a locked change returns `422` `{ "error": "...", "locked": { "": "" } }` and nothing is written, and an accepted change broadcasts `order.changed`. Requires Fleet-Ops with fleetbase/fleetops#394 (release v0.6.72). url: "{{base_url}}/{{namespace}}/payloads/:id" method: PUT pathVariables: diff --git a/postman/collections/Fleetbase Core API/Chat Channels/.resources/object.yaml b/postman/collections/Fleetbase Core API/Chat Channels/.resources/object.yaml index d2afa96..cdb60e3 100644 --- a/postman/collections/Fleetbase Core API/Chat Channels/.resources/object.yaml +++ b/postman/collections/Fleetbase Core API/Chat Channels/.resources/object.yaml @@ -8,10 +8,30 @@ example: | "created_by": "user_8xP2L1d", "name": "Dispatch updates", "title": "Dispatch updates", - "last_message": null, + "last_message": { + "id": "chat_message_3Hk9mL2", + "chat_channel": "chat_channel_5VUl2v4", + "content": "Order updated: Drop-off changed to 8 Marina View", + "sender": null, + "system": true, + "meta": { "system": true, "kind": "order_updated" }, + "attachments": [], + "receipts": [], + "updated_at": "2026-10-13T09:30:00Z", + "created_at": "2026-10-13T09:30:00Z" + }, "slug": "dispatch-updates", "feed": [], - "participants": [], + "participants": [ + { + "id": "chat_participant_2Lm4kQ7", + "name": "Ada Buyer", + "role": "customer", + "meta": { "role": "customer" }, + "is_online": true, + "last_seen_at": "2026-10-13T09:29:00Z" + } + ], "meta": {}, "updated_at": "2026-05-07T09:30:00Z", "created_at": "2026-05-07T09:30:00Z", @@ -32,7 +52,7 @@ fields: description: "Display title for the channel." - name: last_message type: object - description: "Most recent chat message for the channel, when available." + description: "Most recent chat message for the channel, when available. Messages carry `system` (true for messages the system posts, such as `order_updated` or `address_change_requested`, read from `meta.system`) and `meta` (with `kind` for system messages). `sender` is the sending participant, or null for a message without one. Requires Core API with fleetbase/core-api#306 (release v1.6.69)." - name: unread_count type: integer description: "Unread message count for the current session user when session context is available." @@ -44,10 +64,10 @@ fields: description: "Messages and feed entries returned for the channel." - name: participants type: array of objects - description: "Chat participants in the channel." + description: "Chat participants in the channel. Each participant has `role` and `meta`: `role` is read from `meta.role` and is `customer`, `driver`, `store` or `system` when one of those was recorded (order chats record it), else null; `meta` is the participant's metadata (an empty object when unset). Requires Core API with fleetbase/core-api#306 (release v1.6.69)." - name: meta type: object - description: "Arbitrary metadata stored with the channel." + description: "Arbitrary metadata stored with the channel. Storefront order chats carry `storefront_order_uuid`, `storefront_order_id`, `order_public_id` and `kind` (`group` for the customer and driver thread, `customer` for the customer and store thread)." - name: updated_at type: timestamp description: "Time the channel was last updated." diff --git a/postman/collections/Fleetbase Core API/Chat Channels/Send Message.request.yaml b/postman/collections/Fleetbase Core API/Chat Channels/Send Message.request.yaml index 9b35fbb..fbdbbff 100644 --- a/postman/collections/Fleetbase Core API/Chat Channels/Send Message.request.yaml +++ b/postman/collections/Fleetbase Core API/Chat Channels/Send Message.request.yaml @@ -1,6 +1,8 @@ $kind: http-request description: |- Sends a message to a chat channel as an existing chat participant. File IDs can be included to attach previously uploaded files to the message. + + The response is the chat message, with `sender` (the participant, including its `role`), `system` and `meta` (see the Chat Channel object). url: "{{base_url}}/{{namespace}}/chat-channels/:id/send-message" method: POST pathVariables: diff --git a/postman/collections/Fleetbase Core API/Comments/.resources/definition.yaml b/postman/collections/Fleetbase Core API/Comments/.resources/definition.yaml index acc98b9..11c6d92 100644 --- a/postman/collections/Fleetbase Core API/Comments/.resources/definition.yaml +++ b/postman/collections/Fleetbase Core API/Comments/.resources/definition.yaml @@ -1,4 +1,8 @@ $kind: collection description: |- Comments attach discussion to Fleetbase resources or reply to existing comments. Use comment endpoints to create threaded context around orders, files, operational records, and other subject models. + + Comments are `internal` (notes for the organization's own users) or `customer` (may be shown to customers and drivers). The public API only returns customer-visible comments. + + New comments are broadcast as the realtime event `comment.created` on `company.{uuid}` and, for customer-visible comments, on the subject's channels (`{subject type}.{public_id}` and `{subject type}.{uuid}`, e.g. `order.order_7YqM3KpL2n`). Payload: `{ id, event, created_at, subject_id, data }`, where `data` is the comment and `subject_id` the subject's public ID. Requires Core API with fleetbase/core-api#306 (release v1.6.69). order: 4000 diff --git a/postman/collections/Fleetbase Core API/Comments/.resources/object.yaml b/postman/collections/Fleetbase Core API/Comments/.resources/object.yaml index dd2cf79..4fe9373 100644 --- a/postman/collections/Fleetbase Core API/Comments/.resources/object.yaml +++ b/postman/collections/Fleetbase Core API/Comments/.resources/object.yaml @@ -7,7 +7,8 @@ example: | "id": "comment_8JVZr4q", "content": "Please confirm the delivery window.", "tags": [], - "meta": {}, + "meta": { "visibility": "customer" }, + "visibility": "customer", "author": { "id": "user_8xP2L1d", "name": "Alex Rivera" @@ -31,12 +32,16 @@ fields: - name: meta type: object description: "Arbitrary metadata stored with the comment." + - name: visibility + type: enum + values: ["internal", "customer"] + description: "Who may see the comment, from `meta.visibility`: `internal` comments are notes for the organization's own users and never appear on the public API, `customer` comments may be shown to customers and drivers. A comment without a visibility is `customer`. Requires Core API with fleetbase/core-api#306 (release v1.6.69)." - name: author type: object description: "User that authored the comment." - name: replies type: array of objects - description: "Nested replies to the comment." + description: "Nested replies to the comment. On the public API, internal replies are left out." - name: editable type: boolean description: "Whether the current session user can edit the comment." diff --git a/postman/collections/Fleetbase Core API/Comments/Create Comment.params.yaml b/postman/collections/Fleetbase Core API/Comments/Create Comment.params.yaml index 21d751d..2b502af 100644 --- a/postman/collections/Fleetbase Core API/Comments/Create Comment.params.yaml +++ b/postman/collections/Fleetbase Core API/Comments/Create Comment.params.yaml @@ -25,3 +25,7 @@ fields: - name: parent type: string description: "Public ID of the parent comment when creating a reply." + - name: visibility + type: enum + values: ["internal", "customer"] + description: "Who may see the comment; any other value returns `422`. Stored as `meta.visibility`. When omitted, a reply takes its parent's visibility, a comment on an order created through this API is `customer` (and `internal` when written from the console), and a comment on any other subject has none, which counts as `customer`. Requires Core API with fleetbase/core-api#306 (release v1.6.69)." diff --git a/postman/collections/Fleetbase Core API/Comments/Create Comment.request.yaml b/postman/collections/Fleetbase Core API/Comments/Create Comment.request.yaml index a243c3e..3f65c1a 100644 --- a/postman/collections/Fleetbase Core API/Comments/Create Comment.request.yaml +++ b/postman/collections/Fleetbase Core API/Comments/Create Comment.request.yaml @@ -1,6 +1,8 @@ $kind: http-request description: |- Creates a comment on a subject resource or as a reply to an existing comment. Provide either a subject reference or a parent comment ID with the comment content. + + Set `visibility` to `internal` for a note only the organization's users should see, or `customer` for one customers and drivers may see (see the Comment object for the default). Internal comments are not returned by `Query Comments` or `Retrieve Comment`. Every new comment broadcasts the realtime event `comment.created` (see the Comments folder). Requires Core API with fleetbase/core-api#306 (release v1.6.69). url: "{{base_url}}/{{namespace}}/comments" method: POST body: diff --git a/postman/collections/Fleetbase Core API/Comments/Query Comments.request.yaml b/postman/collections/Fleetbase Core API/Comments/Query Comments.request.yaml index f7a19d5..4958fb0 100644 --- a/postman/collections/Fleetbase Core API/Comments/Query Comments.request.yaml +++ b/postman/collections/Fleetbase Core API/Comments/Query Comments.request.yaml @@ -1,6 +1,6 @@ $kind: http-request description: |- - Returns comments for the current organization. Use query parameters to filter, sort, and paginate the result set. + Returns comments for the current organization. Use query parameters to filter, sort, and paginate the result set. Internal comments (`visibility` `internal`) and internal replies are left out. Requires Core API with fleetbase/core-api#306 (release v1.6.69). url: "{{base_url}}/{{namespace}}/comments" method: GET queryParams: diff --git a/postman/collections/Fleetbase Core API/Comments/Retrieve Comment.request.yaml b/postman/collections/Fleetbase Core API/Comments/Retrieve Comment.request.yaml index 5b18054..1924191 100644 --- a/postman/collections/Fleetbase Core API/Comments/Retrieve Comment.request.yaml +++ b/postman/collections/Fleetbase Core API/Comments/Retrieve Comment.request.yaml @@ -1,6 +1,6 @@ $kind: http-request description: |- - Retrieves a comment by ID, including its author and any nested replies returned by the API resource. + Retrieves a comment by ID, including its author and any nested replies returned by the API resource. An internal comment returns `404` (`Comment resource not found.`), and internal replies are left out. Requires Core API with fleetbase/core-api#306 (release v1.6.69). url: "{{base_url}}/{{namespace}}/comments/:id" method: GET pathVariables: diff --git a/postman/collections/Fleetbase Storefront API/Checkout/Before Cash Pickup Checkout.request.yaml b/postman/collections/Fleetbase Storefront API/Checkout/Before Cash Pickup Checkout.request.yaml index 0de5735..da8bf33 100644 --- a/postman/collections/Fleetbase Storefront API/Checkout/Before Cash Pickup Checkout.request.yaml +++ b/postman/collections/Fleetbase Storefront API/Checkout/Before Cash Pickup Checkout.request.yaml @@ -1,6 +1,8 @@ $kind: http-request description: |- Initializes a cash (cash-on-delivery) pickup checkout. A pickup checkout with the cash gateway needs no delivery service quote, so the `service_quote` parameter is omitted. The response returns the checkout id and token; no gateway client data is prepared for cash. + + Like every checkout, it holds stock for products that track inventory and can be refused with `422` `cart_items_unavailable` or `booking_slot_unavailable` (see `Before`). url: "{{base_url}}/{{api_prefix}}/{{namespace}}/checkouts/before" method: GET queryParams: diff --git "a/postman/collections/Fleetbase Storefront API/Checkout/Before \342\235\227.request.yaml" "b/postman/collections/Fleetbase Storefront API/Checkout/Before \342\235\227.request.yaml" index 3d6bacf..6cf514f 100644 --- "a/postman/collections/Fleetbase Storefront API/Checkout/Before \342\235\227.request.yaml" +++ "b/postman/collections/Fleetbase Storefront API/Checkout/Before \342\235\227.request.yaml" @@ -2,6 +2,11 @@ $kind: http-request description: |- Initializes a checkout preview for a cart, customer, gateway, and delivery or pickup option. The response prepares gateway-specific client data or a checkout token before the order is captured. + Stock and booked slots are checked when the checkout is created (cash, Stripe and QPay alike, and again when `Update Stripe Payment Intent` creates or re-prices a checkout). For products that track inventory, each line's quantity is held at the line's store location or food truck for 15 minutes and taken when the order is captured; an abandoned checkout gives it back. A refusal deletes the checkout: + + - `422` `{ "errors": ["Some items in your cart are no longer available."], "code": "cart_items_unavailable", "items": [{ "id", "product_id", "name", "available" }] }` when a tracked product doesn't have enough stock at the line's location. `id` is the cart line and `available` is what is left to sell there. + - `422` `{ "errors": ["The time you picked for was just booked. Please pick another time."], "code": "booking_slot_unavailable" }` when a booked service's slot (see `List Product Booking Slots`) has no places left. + Promotions are priced when the checkout is created: automatic promotions and any promo codes (from `promo_codes` or applied to the cart) are subtracted from the amount charged, and each use is reserved for the checkout. A code that cannot be applied refuses the checkout with `400`. The response depends on the gateway: cash returns `{ checkout, token }`, Stripe returns `{ paymentIntent, clientSecret, ephemeralKey, customerId, checkout, token }`, and QPay returns `{ invoice, checkout, token }`. When realtime socket authentication is enabled, every response also carries `socket_token`: a token scoped to the realtime channel `checkout.{checkout}`, which a guest uses to receive payment and order updates (see the Checkout object). diff --git a/postman/collections/Fleetbase Storefront API/Notifications/.resources/object.yaml b/postman/collections/Fleetbase Storefront API/Notifications/.resources/object.yaml index 5d183c1..b66c148 100644 --- a/postman/collections/Fleetbase Storefront API/Notifications/.resources/object.yaml +++ b/postman/collections/Fleetbase Storefront API/Notifications/.resources/object.yaml @@ -1,7 +1,7 @@ $kind: object name: Notification description: |- - An inbox item. `type` identifies what the notification is about (for example `order_accepted`, `order_completed`, `promotional` or `campaign`), and `data` carries the identifiers the app needs to deep link. Recipient details are never included. + An inbox item. `type` identifies what the notification is about (for example `order_accepted`, `order_updated`, `order_item_unavailable`, `order_completed`, `promotional` or `campaign`), and `data` carries the identifiers the app needs to deep link. Recipient details are never included. example: | { "id": "0f4c9a52-8d3f-4a6e-9f0e-2b1f7f2f8c11", @@ -24,13 +24,13 @@ fields: description: "Notification id (uuid)." - name: type type: string - description: "What the notification is about, e.g. `order_accepted`, `order_enroute`, `order_nearby`, `order_completed`, `order_canceled`, `order_ready`, `order_driver_assigned`, `order_activity`, `order_chat_message`, `promotional` or `campaign`. `order_activity` covers order steps that have no notification of their own (and every step of a booking after it's requested); its title is the step's label. `order_chat_message` is a message from the driver (`data` has `order`, `order_id`, `chat_id`, `message_id`, `store_id`, `network_id`); it's always delivered, whatever the `order_updates` preference." + description: "What the notification is about, e.g. `order_accepted`, `order_enroute`, `order_nearby`, `order_completed`, `order_canceled`, `order_ready`, `order_driver_assigned`, `order_activity`, `order_chat_message`, `order_updated` (the store edited the order or moved a booking), `order_item_unavailable` (the store marked an item unavailable), `order_balance_paid` (a balance due was paid by its payment link), `promotional` or `campaign`. `order_activity` covers order steps that have no notification of their own (and every step of a booking after it's requested); its title is the step's label. `order_chat_message` is a message from the driver (`data` has `order`, `order_id`, `chat_id`, `message_id`, `store_id`, `network_id`); it's always delivered, whatever the `order_updates` preference." - name: title type: string description: "Notification title." - name: body type: string - description: "Notification message." + description: "Notification message. For `order_updated` (the store edited the order) it says what changed and what that means for the customer's money: refunded, returned as store credit with the code to use, or a balance to pay with its payment link. A booking that was only moved says when it is now, in the store's time (`Deep Clean is now Mon 19 Oct, 11:00 to 12:00.`). For `order_item_unavailable` it names the item and says the store will replace or remove it and adjust the total. For `order_balance_paid` it gives the amount received and anything still left to pay. For `order_canceled` it gives the store's reason, when there is one, and whether the payment was refunded or returned as store credit." - name: image type: string description: "Optional image URL." diff --git a/postman/collections/Fleetbase Storefront API/Orders/.resources/object.yaml b/postman/collections/Fleetbase Storefront API/Orders/.resources/object.yaml index 93b280f..2776ce5 100644 --- a/postman/collections/Fleetbase Storefront API/Orders/.resources/object.yaml +++ b/postman/collections/Fleetbase Storefront API/Orders/.resources/object.yaml @@ -6,23 +6,81 @@ example: | { "id": "order_9Kx2mQ1", "public_id": "order_9Kx2mQ1", - "status": "created", + "status": "preparing", "type": "storefront", - "payload": null, + "payload": { + "id": "payload_4Rt7nB2", + "entities": [ + { + "id": "entity_6Lp2wQ9", + "name": "Cold Brew", + "meta": { "product_id": "product_9Lb2mQ1", "quantity": 2, "note": "No ice, please", "unavailable": false } + }, + { + "id": "entity_3Hd8vK5", + "name": "Iced Latte", + "meta": { "product_id": "product_7Qa1zX4", "quantity": 1, "substitute_for": "entity_8Wn5cT1", "added_by_edit": true } + } + ] + }, "customer": null, "meta": { "checkout_id": "checkout_123", "storefront": "Acme Market", - "total": 5150, + "total": 4600, "discount": 500, "promotions": [ { "promotion": "promotion_7Hq2xL9", "name": "Welcome $5 off", "type": "fixed_amount", "code": "WELCOME5", "amount": 500, "delivery_amount": 0 } ], - "currency": "USD" + "currency": "USD", + "gateway": "stripe", + "payment": { + "state": "partially_refunded", + "gateway": "stripe", + "method_label": "Card (Stripe)", + "total": 4600, + "amount_paid": 5150, + "amount_refunded": 550, + "amount_credited": 0, + "balance_due": 0, + "cod_amount": 0, + "currency": "USD", + "payment_link": null, + "store_credit_code": null, + "supports_partial_refund": true, + "adjustments": [ + { "type": "refund", "amount": 550, "gateway": "stripe", "reference": "re_example123", "reason": "Iced Mocha was unavailable", "at": "2026-10-13T09:40:00+00:00" } + ] + }, + "address_change_request": { + "status": "open", + "message": "Please deliver to my office instead.", + "place": "place_2Vb6nM8", + "address": "1 Raffles Place, Singapore 048616", + "at": "2026-10-13T09:45:00+00:00" + } + }, + "payment": { + "state": "partially_refunded", + "gateway": "stripe", + "method_label": "Card (Stripe)", + "total": 4600, + "amount_paid": 5150, + "amount_refunded": 550, + "amount_credited": 0, + "balance_due": 0, + "cod_amount": 0, + "currency": "USD", + "payment_link": null, + "store_credit_code": null, + "supports_partial_refund": true, + "adjustments": [ + { "type": "refund", "amount": 550, "gateway": "stripe", "reference": "re_example123", "reason": "Iced Mocha was unavailable", "at": "2026-10-13T09:40:00+00:00" } + ] }, "notes": null, - "created_at": "2026-05-07T09:30:00Z", - "updated_at": "2026-05-07T09:30:00Z" + "created_at": "2026-10-13T09:30:00Z", + "updated_at": "2026-10-13T09:45:00Z" } fields: - name: id @@ -55,3 +113,24 @@ fields: - name: updated_at type: timestamp description: "Time the order was last updated." + - name: payment + type: object + description: "The order's payment, computed when the order is returned (customer order lists). Same shape as `meta.payment`." + - name: meta.payment + type: object + description: "The order's payment as one picture, stored on the order and kept current by the server at capture, cash collection, edits and cancellation, so it is present whichever resource returns the order. `{ state, gateway, method_label, total, amount_paid, amount_refunded, amount_credited, balance_due, cod_amount, currency, payment_link, store_credit_code, supports_partial_refund, adjustments }`. `state` is `paid`, `to_collect` (cash on delivery not collected yet), `unpaid`, `refunded`, `partially_refunded` or `balance_due`. `payment_link` is the link to pay a balance due (Stripe Payment Link or QPay invoice); `store_credit_code` is the single-use code issued when a difference was returned as store credit. `adjustments` lists each change after checkout: `{ type, amount, gateway, reference, reason, at }` (and `by` when made by a store user), where `type` is `refund`, `store_credit`, `charge` or `cod_update`. Amounts are in the currency's smallest unit. A cash-on-delivery order stays `to_collect` with `amount_paid` 0 until it is completed." + - name: meta.booking + type: object + description: "Booking orders only: the booked slot `{ start, end, duration_minutes }` (ISO 8601), alongside `is_booking` and `booking_at`." + - name: meta.cancel_reason + type: string + description: "Why the store canceled the order, when it gave a reason." + - name: meta.tip_amount + type: integer + description: "Tip for the store, in the currency's smallest unit (`meta.delivery_tip_amount` for the driver's tip)." + - name: meta.address_change_request + type: object + description: "Set by `Request Address Change`: `{ status, message, place, address, at }`. `status` is `open` until the store updates the address (`resolved`) or dismisses the request (`dismissed`), which also adds `closed_at`; `place` and `address` are the saved place the customer suggested, or null." + - name: payload.entities[].meta + type: object + description: "Per item, besides the cart line data (`product_id`, `quantity`, ...): `note` (the store's note on the item), `unavailable` (the store marked it unavailable; every other item is taken as packed and delivered), `substitute_for` (the entity id this item replaces) and `added_by_edit` (added when the store edited the order)." diff --git a/postman/collections/Fleetbase Storefront API/Orders/Balance Paid Callback.queryParams.yaml b/postman/collections/Fleetbase Storefront API/Orders/Balance Paid Callback.queryParams.yaml new file mode 100644 index 0000000..96ecca3 --- /dev/null +++ b/postman/collections/Fleetbase Storefront API/Orders/Balance Paid Callback.queryParams.yaml @@ -0,0 +1,5 @@ +$kind: params +fields: + - name: order + type: string + description: "Public ID of the order whose balance invoice was paid. QPay may also POST it in the body; either works." diff --git a/postman/collections/Fleetbase Storefront API/Orders/Balance Paid Callback.request.yaml b/postman/collections/Fleetbase Storefront API/Orders/Balance Paid Callback.request.yaml new file mode 100644 index 0000000..5329337 --- /dev/null +++ b/postman/collections/Fleetbase Storefront API/Orders/Balance Paid Callback.request.yaml @@ -0,0 +1,24 @@ +$kind: http-request +description: |- + QPay's callback for a balance invoice: when the store edits a paid order and the total goes up, the customer is sent a payment link for the balance (a QPay invoice, or a Stripe Payment Link), and QPay calls this URL when the invoice is paid. It needs no storefront key; Storefront builds the URL with the order's public id when it creates the invoice. + + Storefront never trusts the callback itself: it asks the gateway what was paid against the order's open balance link. When the balance is paid it records the charge on the order's payment (`meta.payment`), closes the link and sends the customer the `order_balance_paid` notification. Stripe Payment Links have no callback; Storefront checks them every five minutes (`storefront:check-balance-payments`). + + Response: `{ "status": "paid" }` when the balance was settled now, `{ "status": "pending" }` when nothing (more) is paid or the gateway can't be asked, and `404` with `{ "status": "unknown" }` for an unknown order. Calling it again after the balance is settled answers `pending`. + + In a contract run this addresses `{{balance_order_id}}` when the environment sets one. Otherwise it addresses an unknown order and asserts the `404`. +url: "{{base_url}}/{{api_prefix}}/{{namespace}}/orders/balance-paid" +method: GET +queryParams: + order: "{{balance_order_id}}" +scripts: + - type: beforeRequest + code: |- + // A contract run has no order with an open balance link: without one this asks about + // an order that doesn't exist and asserts the 404. + if (!pm.environment.get('balance_order_id')) { + pm.variables.set('balance_order_id', 'order_missing'); + pm.variables.set('expected_status', 404); + } + language: text/javascript +order: 3000 diff --git a/postman/collections/Fleetbase Storefront API/Orders/Request Address Change.params.yaml b/postman/collections/Fleetbase Storefront API/Orders/Request Address Change.params.yaml new file mode 100644 index 0000000..77de287 --- /dev/null +++ b/postman/collections/Fleetbase Storefront API/Orders/Request Address Change.params.yaml @@ -0,0 +1,8 @@ +$kind: params +fields: + - name: message + type: string + description: "What is wrong with the address, up to 1000 characters." + - name: place + type: string + description: "Public ID of one of the customer's saved places to suggest as the new drop-off. An unknown place, or one that isn't the customer's, returns `404`." diff --git a/postman/collections/Fleetbase Storefront API/Orders/Request Address Change.request.yaml b/postman/collections/Fleetbase Storefront API/Orders/Request Address Change.request.yaml new file mode 100644 index 0000000..ce1746b --- /dev/null +++ b/postman/collections/Fleetbase Storefront API/Orders/Request Address Change.request.yaml @@ -0,0 +1,42 @@ +$kind: http-request +description: |- + Tells the store that the order's drop-off address is wrong. The store sees an alert on the order, and the message is posted in the customer's chat with the store (a message with `kind` `address_change_requested`). The store then updates the address or dismisses the request. Requires the `Customer-Token` of the customer who placed the order. + + Send a `message`, a `place` (one of the customer's saved places, see `customers/places`), or both. The request is recorded on the order as `meta.address_change_request`. + + Response: `{ "status": "ok", "address_change_request": { "status": "open", "message", "place", "address", "at" } }`. `place` and `address` are the suggested saved place's id and address, or null. + + Errors: `401` without a customer token; `404` for an order not placed through this storefront (`Order not found.`) or an unknown `place` (`Place not found.`); `403` for another customer's order; `409` with `"reason": "chat_unavailable"` when the chat with the store can't be opened (the customer has no user account, or the order finished without one); `422` when the order is completed, canceled or expired, or is a pickup order (`The address of this order can no longer change.`), and when `message` is longer than 1000 characters. + + In a contract run this addresses `{{address_change_order_id}}` when the environment sets one (an open delivery order of the run's customer). Otherwise it addresses an unknown order and asserts the `404`. +url: "{{base_url}}/{{api_prefix}}/{{namespace}}/orders/{{address_change_order_id}}/address-change-request" +method: POST +headers: + - key: Customer-Token + value: "{{customer_token}}" + - key: Content-Type + value: application/json +body: + type: json + content: |- + { + "message": "The building number is wrong, it's 18 not 81.", + "place": "{{place_id}}" + } +scripts: + - type: beforeRequest + code: |- + // Needs an open delivery order of the run's customer, which a contract run doesn't + // create (its one captured order is a pickup). Without one it addresses an order + // that doesn't exist and asserts the 404 refusal instead. + if (!pm.environment.get('address_change_order_id')) { + pm.variables.set('address_change_order_id', 'order_missing'); + pm.variables.set('expected_status', 404); + } + + // `place` is optional: send none rather than an unresolved "{{place_id}}". + if (!pm.environment.get('place_id')) { + pm.request.body.update(JSON.stringify({ message: "The building number is wrong, it's 18 not 81." }, null, 2)); + } + language: text/javascript +order: 2500 diff --git a/postman/collections/Fleetbase Storefront API/Products/.resources/object.yaml b/postman/collections/Fleetbase Storefront API/Products/.resources/object.yaml index 0db622c..a0de94a 100644 --- a/postman/collections/Fleetbase Storefront API/Products/.resources/object.yaml +++ b/postman/collections/Fleetbase Storefront API/Products/.resources/object.yaml @@ -17,6 +17,17 @@ example: | "is_service": false, "is_bookable": false, "is_available": true, + "track_inventory": true, + "inventory": { + "tracked": true, + "in_stock": true, + "available_total": 3, + "low": true, + "locations": [ + { "id": "inventory_4Kd9wP2", "location": "store_location_7Nx2bQ5", "location_type": "store_location", "quantity": 5, "reserved": 2, "available": 3, "low_stock_threshold": 5, "allow_backorder": false, "low": true } + ] + }, + "in_stock": true, "tags": ["coffee"], "status": "published", "meta": {}, @@ -71,6 +82,15 @@ fields: - name: is_available type: boolean description: "Whether the product is available for purchase." + - name: track_inventory + type: boolean + description: "Whether the store tracks stock for this product. Off by default; products that don't track stock are never limited by it." + - name: inventory + type: object + description: "Stock as clients show it: `{ tracked, in_stock, available_total, low, locations }`. Untracked products return `{ \"tracked\": false, \"in_stock\": true, \"available_total\": null, \"low\": false, \"locations\": [] }`. Each location is `{ id, location, location_type, quantity, reserved, available, low_stock_threshold, allow_backorder, low }`: `location` is the store location or food truck public ID and `location_type` is `store_location` or `food_truck`; `quantity` is on hand, `reserved` is held by checkouts in progress (for 15 minutes), `available` is what can still be sold, and `low` is true while some stock is available but no more than `low_stock_threshold`. `in_stock` is true when any location has stock available or allows backorders." + - name: in_stock + type: boolean + description: "Whether the product can be sold now: false when `is_available` is false, or when it tracks stock and no location has any available (or allows backorders). Use it for out of stock labels; `inventory.available_total` for \"only N left\"." - name: tags type: array of strings description: "Product tags." diff --git a/postman/collections/Fleetbase Storefront API/Products/List Product Booking Slots.queryParams.yaml b/postman/collections/Fleetbase Storefront API/Products/List Product Booking Slots.queryParams.yaml new file mode 100644 index 0000000..87452cd --- /dev/null +++ b/postman/collections/Fleetbase Storefront API/Products/List Product Booking Slots.queryParams.yaml @@ -0,0 +1,6 @@ +$kind: params +fields: + - name: date + type: string + required: true + description: "The day to list, `YYYY-MM-DD`, in the store's time zone." diff --git a/postman/collections/Fleetbase Storefront API/Products/List Product Booking Slots.request.yaml b/postman/collections/Fleetbase Storefront API/Products/List Product Booking Slots.request.yaml new file mode 100644 index 0000000..d2d761b --- /dev/null +++ b/postman/collections/Fleetbase Storefront API/Products/List Product Booking Slots.request.yaml @@ -0,0 +1,31 @@ +$kind: http-request +description: |- + Returns the times a bookable service can be booked on a day. Slots are the service's hours for that weekday (else the store's first location's hours), cut into slots of the service's length, each with how many places are left. Times are in the store's time zone (else the company's, else the server's). Bookings of the service take `quantity` places in their slot; canceled, expired and failed orders give their places back. + + Response: `{ date, service: { duration_minutes, capacity, deposit_percent, travel_fee, provider_tag }, slots: [{ start, end, time, past, available, remaining, providers, capacity }] }`. `start` and `end` are ISO 8601 with the store's offset and `time` is the store's local `HH:mm`. `past` is true once the slot has started; `available` is true when the slot is still ahead and has places left; `remaining` (also reported as `providers`) is the places left out of `capacity`. `duration_minutes` defaults to 60 and `capacity` to 1. `deposit_percent` and `travel_fee` are reported as configured on the service but are not charged at checkout. + + Book a slot by adding the service to the cart with its `start` as the line's `scheduled_at`. If a slot the service's hours define fills up before checkout, initializing the checkout returns `422` with `"code": "booking_slot_unavailable"`. + + Errors: `404` (`Bookable service not found.`) when the product isn't bookable or isn't in this store or network; `422` when `date` is missing or not `YYYY-MM-DD`. + + In a contract run this addresses `{{service_product_id}}` when the environment sets one (a bookable service with hours). Otherwise it addresses the run's product, which isn't bookable, and asserts the `404`. +url: "{{base_url}}/{{api_prefix}}/{{namespace}}/products/{{service_product_id}}/slots" +method: GET +queryParams: + date: "{{slot_date}}" +scripts: + - type: beforeRequest + code: |- + // The day to list: the environment's {{slot_date}}, else today (UTC). + if (!pm.environment.get('slot_date')) { + pm.variables.set('slot_date', new Date().toISOString().slice(0, 10)); + } + + // A contract run has no bookable service unless the environment names one; the + // run's own product is a plain item, so the request documents the 404 instead. + if (!pm.environment.get('service_product_id')) { + pm.variables.set('service_product_id', pm.environment.get('product_id') || 'product_missing'); + pm.variables.set('expected_status', 404); + } + language: text/javascript +order: 3500