# BipBip API — JSON Payload Schemas Schema version: `1.0` (carried in `X-Bipbip-Schema-Version`). BipBip may add new optional fields and new enum values without incrementing the schema version. Implementations must tolerate unknown fields and unknown enum values gracefully (respond 200 and ignore). --- ## Order Payload delivered by BipBip to the POS in `POST /v1/order/{remoteId}` (`X-Bipbip-Event-Type: order.created`). Designed under a data-minimization principle: only fields the POS needs to cook, bill, and collect are included. BipBip internal IDs (storeId, customerId) are intentionally absent. | Field | Type | Required | Description | |-------|------|----------|-------------| | `orderKey` | string | yes | Opaque BipBip order identifier. Format: `ord_` + 16 base62 characters. Use this value in all REST API calls to accept, reject, or advance the order. | | `displayCode` | string | yes | Human-readable order code shown to the customer in the BipBip app (e.g., `"355156479"`). Useful for support references. Not globally unique — combine with `storeRemoteId` if global uniqueness is required. | | `storeRemoteId` | string | yes | Store identifier configured during onboarding. Same value as `{remoteId}` in the webhook URL path. Included in the body as a fallback if logs truncate the URL. | | `currency` | string | yes | ISO 4217 currency code (e.g., `"HNL"`). | | `createdAt` | ISO 8601 | yes | UTC timestamp of when the order was created in BipBip. | | `expiresAt` | ISO 8601 | yes | Acceptance deadline. If the order is not accepted before this timestamp, BipBip auto-cancels it and sends a cancellation webhook. | | `fulfillment` | Fulfillment | yes | Fulfillment type and timing. See Fulfillment schema below. | | `customer` | Customer \| null | no | Customer info. Populated only for `pickup` orders so staff can call the customer to the counter. `null` for `delivery` orders. | | `payment` | Payment | yes | Payment methods and amount to collect at handover. See Payment schema below. | | `summary` | Summary | yes | Aggregated totals for quick reconciliation. See Summary schema below. | | `charges` | Charge[] | yes | Itemized additional charges (delivery fee, express fee, etc.). Empty array if none. | | `discounts` | Discount[] | yes | Applied discounts. Empty array if none. | | `items` | Item[] | yes | Order line items. At least one item is always present. | | `customerNote` | string \| null | no | Free-text note from the customer for the entire order (e.g., "leave at reception"). `null` if absent. | | `invoice` | Invoice \| null | no | Tax credit data. Populated when the customer requested a fiscal invoice with their tax ID. `null` for consumer-final orders (majority case). | --- ## Fulfillment Nested inside `Order.fulfillment`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `type` | enum | yes | `delivery` (BipBip driver picks up and delivers) or `pickup` (customer comes to the store). | | `isExpress` | boolean | yes | `true` if the customer paid for express delivery — prioritize in kitchen. Always `false` for `pickup` orders. | | `prepareBy` | ISO 8601 | yes | UTC timestamp by which the order must be ready. Kitchen schedules against this value. | | `driverPickupAt` | ISO 8601 \| null | no | UTC timestamp of when the BipBip driver is expected to arrive at the store. Required when `type=delivery`; `null` for `pickup`. | | `customerPickupAt` | ISO 8601 \| null | no | UTC timestamp of when the customer expects to arrive at the store. Required when `type=pickup`; `null` for `delivery`. | --- ## Customer Nested inside `Order.customer`. Only present for `pickup` orders. | Field | Type | Required | Description | |-------|------|----------|-------------| | `firstName` | string | yes | Customer's first name. Used by staff to call the customer to the counter ("Order for Ana"). Last name, phone, and email are intentionally excluded (data minimization). | --- ## Payment Nested inside `Order.payment`. Payment semantics: `cash` is collected by the merchant at handover. `card` and `bips` are pre-paid through the BipBip app — do not collect them again. | Field | Type | Required | Description | |-------|------|----------|-------------| | `methods` | PaymentMethod[] | yes | List of payment methods applied to the order. The sum of all `amount` values equals `summary.grandTotal`. At least one method is always present. | | `amountToCollect` | decimal | yes | Amount the merchant must collect from the customer at handover. Equals the sum of `methods[].amount` where `type = "cash"`. `0.00` if the order is fully pre-paid. | | `changeFor` | decimal \| null | no | Denomination of the bill the customer plans to use for cash payment — for change preparation. Present only when at least one method is `cash`; `null` for fully pre-paid orders. | ### PaymentMethod Nested inside `Payment.methods[]`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `type` | enum | yes | `cash` (collect at handover), `card` (pre-paid via BipBip app), or `bips` (pre-paid via BipBip loyalty credits). | | `amount` | decimal | yes | Amount applied with this payment method. | --- ## Summary Nested inside `Order.summary`. All values are positive decimals. Formula: `grandTotal = subtotal + taxes - discounts + additionalCharges` | Field | Type | Required | Description | |-------|------|----------|-------------| | `subtotal` | decimal | yes | Sum of `unitPrice * quantity` across all items, before taxes. This is the merchant's revenue line. | | `taxes` | decimal | yes | Sum of `tax * quantity` across all items. | | `discounts` | decimal | yes | Sum of `discounts[].amount` (positive value). | | `additionalCharges` | decimal | yes | Sum of `charges[].amount`. | | `grandTotal` | decimal | yes | Total paid by the customer. Equals the sum of `payment.methods[].amount`. | --- ## Charge Elements of `Order.charges[]`. Represents additional charges added to the customer's total. | Field | Type | Required | Description | |-------|------|----------|-------------| | `code` | enum | yes | Charge type. Closed enum: `delivery_fee`, `express_fee`, `service_fee`, `driver_tip`, `small_order_fee`. New codes will be added only with a major schema bump and advance notice. | | `amount` | decimal | yes | Charge amount (positive value). | | `billedBy` | enum | yes | `bipbip` (billed by BipBip, not merchant revenue) or `merchant` (merchant revenue). | --- ## Discount Elements of `Order.discounts[]`. Represents discounts applied to the order. | Field | Type | Required | Description | |-------|------|----------|-------------| | `code` | string | yes | Discount identifier. In v1.0, BipBip emits a single generic entry with `code="GENERIC"` when `discounts > 0`. Future iterations will break this out into coupon codes, loyalty levels, etc. | | `name` | string | yes | Human-readable discount label. | | `amount` | decimal | yes | Discount amount (positive value). | | `fundedBy` | enum | yes | `bipbip` (BipBip funds the promotion; merchant receives full price) or `merchant` (merchant funds the promotion; impacts merchant revenue). | --- ## Invoice Nested inside `Order.invoice`. Only present when the customer requested a fiscal invoice (tax credit). | Field | Type | Required | Description | |-------|------|----------|-------------| | `taxId` | string | yes | Customer's RTN (Honduras tax ID). | | `businessName` | string | yes | Customer's business legal name. | When the `invoice` object is present, both `taxId` and `businessName` are always populated. --- ## Item Elements of `Order.items[]`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `code` | string | yes | Product code in the BipBip catalog. Stable opaque identifier. May be empty if the product was registered without a code — treat an empty value as a data quality signal. | | `remoteCode` | string | yes | Product code in the POS catalog, as configured by the brand in BipBip. Falls back to `code` (BipBip catalog) if no `remoteCode` was configured for the product. | | `name` | string | yes | Visible product name. | | `quantity` | integer | yes | Ordered quantity (minimum 1). | | `unitPrice` | decimal | yes | Unit price before taxes. | | `tax` | decimal | yes | Tax amount per unit of the item. | | `lineTotal` | decimal | yes | Line total: `(unitPrice + tax) * quantity` plus modifier option contributions. Provided for quick reconciliation; the POS can recalculate it locally. | | `note` | string \| null | no | Customer instruction for this item (e.g., "no onion"). `null` if no note. | | `modifierGroups` | ItemModifierGroup[] | yes | Modifier groups applied to this item. Empty array if no modifiers were selected. | --- ## ItemModifierGroup Elements of `Item.modifierGroups[]`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `code` | string | yes | Modifier group code in the BipBip catalog. | | `name` | string | yes | Visible group name (e.g., `"Extras"`). | | `options` | ModifierOption[] | yes | Selected options within this group. At least one option is always present. | ### ModifierOption Elements of `ItemModifierGroup.options[]`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `code` | string | yes | Option code in the BipBip catalog. | | `remoteCode` | string | yes | Option code in the POS catalog. Falls back to `code` if no `remoteCode` was configured. | | `name` | string | yes | Visible option name. | | `quantity` | integer | yes | Number of units of this option selected (minimum 1). | | `unitPrice` | decimal | yes | Unit price of the option. `0.00` if the option is included in the base item price (no-cost upgrade). | --- ## OrderAck The response body the POS must return to BipBip in the `200 OK` of the creation webhook. ```json { "remoteOrderId": "POS-2026-04-11-00142" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `remoteOrderId` | string | yes | The POS's internal order ID for this order. BipBip stores this and uses it to build the URL for all subsequent update webhooks: `PUT /v1/order/{remoteId}/{remoteOrderId}/events`. A `200 OK` response without this field is treated as a failed delivery and will be retried. | --- ## OrderUpdateEvent Delivered by BipBip to `PUT /v1/order/{remoteId}/{remoteOrderId}/events`. `event` describes what happened, not the state of the resource. Only `cancelled` and `delivered` have a counterpart in the order status; `driver_assigned` and `driver_released` belong to the driver cycle and do not modify it. | Field | Type | Required | Description | |-------|------|----------|-------------| | `orderKey` | string | yes | BipBip order identifier (`ord_` + 16 base62). Can be used to call the REST API if needed. | | `remoteOrderId` | string | yes | POS internal order ID (same value from the original `OrderAck`). Included for convenience; the path parameter is authoritative. | | `event` | string | yes | Event discriminator. Current values: `cancelled` (modifies the status), `driver_assigned` (does not), `driver_released` (does not), `delivered` (modifies the status). New values may be added without a schema version bump — respond 200 and ignore unknown values. | | `occurredAt` | ISO 8601 | yes | UTC timestamp of when the update occurred. | | `reason` | string \| null | no | Informative label contextualizing the update. Present when `event=cancelled` or `event=driver_released`; absent otherwise. See webhooks.md for known values. | | `driver` | OrderEventDriver \| null | no | Present and populated when `event=driver_assigned`; absent for all other events. | | `releasedDriver` | OrderEventDriver \| null | no | Present and populated when `event=driver_released`; absent for all other events. Same type as `driver`. | | `expectedNext` | string \| null | no | Hint for the next expected event. Present when `event=driver_released`. Current value: `driver_reassignment`. Tolerate unknown values. | ### OrderEventDriver Shape used for both `driver` (in `driver_assigned` events) and `releasedDriver` (in `driver_released` events). | Field | Type | Required | Description | |-------|------|----------|-------------| | `fullName` | string | yes | Driver's full name as registered in BipBip. | | `phone` | string \| null | no | Driver's phone number in international format. Absent or `null` if the driver has not registered a number or has marked it private. | --- ## MenuChangeCompletedWebhook Delivered by BipBip to `POST /v1/menu/change/{changeKey}`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `changeKey` | string | yes | Opaque change request identifier. Format: `chg_` + 16 base62 characters. Matches the path parameter and the `changeId` from the original `202 Accepted` if the change was submitted via REST. | | `status` | enum | yes | Aggregate result: `applied` (all stores succeeded), `partial` (some stores failed), or `failed` (all stores failed). | | `submittedAt` | ISO 8601 | yes | UTC timestamp of when the change request was submitted. | | `completedAt` | ISO 8601 | yes | UTC timestamp of when BipBip finished processing the change. | | `results` | MenuChangeResult[] | yes | Per-store, per-entity results. One entry per (store, entity) pair attempted. | --- ## MenuChangeResult Elements of `MenuChangeCompletedWebhook.results[]`. | Field | Type | Required | Description | |-------|------|----------|-------------| | `storeRemoteId` | string | yes | Store identifier where the change was attempted. Same value as configured in the BipBip back-office. | | `targetEntity` | string | yes | Type of menu entity affected. Values in use: `product`, `modifierOption`, `localProduct`. | | `targetCode` | string | yes | Code of the affected entity in the catalog. | | `applied` | boolean | yes | `true` if the change was successfully applied at this store; `false` if it failed. | | `errorCode` | string \| null | no | Machine-readable error code. Present only when `applied=false`. **Omitted from JSON when `applied=true`** (not serialized, not `null`). | | `errorMessage` | string \| null | no | Human-readable error description. Present only when `applied=false`. **Omitted from JSON when `applied=true`** (not serialized, not `null`). | When `applied=true`, neither `errorCode` nor `errorMessage` appears in the JSON object at all. Do not assume their presence. --- ## MerchantErrorResponse Suggested shape for error responses from the merchant's webhook endpoints to BipBip. BipBip only uses the HTTP status code to determine retry behavior — the body is informative. Any JSON shape or an empty body is acceptable; this schema is a recommendation for consistency. | Field | Type | Required | Description | |-------|------|----------|-------------| | `error` | string | no | Short machine-readable error code (e.g., `"invalid_payload"`). | | `message` | string | no | Human-readable error description for debugging. | | `timestamp` | ISO 8601 | no | When the error was generated on the merchant's server. |